// 判断是否是对象类型
function isObject(value) {
  const valueType = typeof value;
  return (value !== null) && (valueType === "object" || valueType === "function")
}

function deepClone(originValue, map = new WeakMap()) {
  // 判断是否是一个Set类型(判断是否为真实某个构造函数的类型，通过instanceof)
  if (originValue instanceof Set) {
    return new Set([...originValue]);
  }

  // 判断是否是一个Map类型
  if (originValue instanceof Map) {
    return new Map([...originValue]);
  }

  // 判断如果是Symbol的value, 那么创建一个新的Symbol。因为Symbol是唯一的，深拷贝时需要创建新的
  if (typeof originValue === "symbol") {
    return Symbol(originValue.description);
  }

  // 判断如果是函数类型, 那么直接使用同一个函数
  if (typeof originValue === "function") {
    return originValue;
  }

  // 判断传入的originValue是否是一个对象类型
  if (!isObject(originValue)) {
    return originValue;
  }

  // 解决循环引用
  // 检查是否已经存在于 WeakMap 中，存在则直接取出来使用，避免重复拷贝（处理循环引用）
  if (map.has(originValue)) {
    return map.get(originValue);
  }

  // 判断传入的对象是数组, 还是对象。因为对象与数组的数据结构稍有不同，并不完全一致
  const newObject = Array.isArray(originValue) ? [] : {};

  // 将原对象和新对象的引用关系存储到 WeakMap 中，以键值对形式
  // 如果在后续递归中再次遇到相同的对象，就可以直接使用，而不需要再次拷贝
  map.set(originValue, newObject);

  // 递归拷贝属性
  for (const key in originValue) {
    if (originValue.hasOwnProperty(key)) {
      newObject[key] = deepClone(originValue[key], map);
    }
  }

  // 对 Symbol 的 key 进行特殊处理
  const symbolKeys = Object.getOwnPropertySymbols(originValue);
  for (const sKey of symbolKeys) {
    newObject[sKey] = deepClone(originValue[sKey], map);
  }

  // 返回通过递归深层遍历赋值后，全新的数据对象
  return newObject;
}
